/*
 * Copyright (C) 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ohos.ringdroid.core;

import com.ohos.ringdroid.core.soundfile.SoundFile;
import ohos.media.audio.*;

import java.nio.ShortBuffer;


public class SamplePlayer {
    public interface OnCompletionListener {
        public void onCompletion();
    }

    private ShortBuffer mSamples;
    private int mSampleRate;
    private int mChannels;
    private int mNumSamples;  // Number of samples per channel.
    private AudioRenderer mAudioCapturer;
    private short[] mBuffer;
    private int mPlaybackStart;  // Start offset, in samples.
    private Thread mPlayThread;
    private boolean mKeepPlaying;
    private OnCompletionListener mListener;

    public SamplePlayer(ShortBuffer samples, int sampleRate, int channels, int numSamples) {
        mSamples = samples;
        mSampleRate = sampleRate;
        mChannels = channels;
        mNumSamples = numSamples;
        mPlaybackStart = 0;

        int bufferSize = AudioRenderer.getMinBufferSize(
                mSampleRate,
                AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT,
                mChannels == 1 ? AudioStreamInfo.ChannelMask.CHANNEL_OUT_MONO : AudioStreamInfo.ChannelMask.CHANNEL_OUT_STEREO);
        // make sure minBufferSize can contain at least 1 second of audio (16 bits sample).
        if (bufferSize < mChannels * mSampleRate * 2) {
            bufferSize = mChannels * mSampleRate * 2;
        }
        mBuffer = new short[bufferSize / 2]; // bufferSize is in Bytes.

        AudioStreamInfo asi = new AudioStreamInfo.Builder()
                .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
                .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_STEREO)
                .sampleRate(mSampleRate)
                .audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK)
                .streamUsage(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA)
                .build();

        AudioRendererInfo audioRenderInfo = new AudioRendererInfo.Builder()
                .audioStreamInfo(asi)
                .audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
                .bufferSizeInBytes(bufferSize)
                .isOffload(false) // false表示分段传输buffer并播放，true表示整个音频流一次性传输到HAL层播放
                .build();
        mAudioCapturer = new AudioRenderer(audioRenderInfo, AudioRenderer.PlayMode.MODE_STREAM);
        // Check when player played all the given data and notify user if mListener is set.
//        mAudioCapturer.setNotificationMarkerPosition(mNumSamples - 1);  // Set the marker to the end.
//
//        mAudioCapturer.setPlaybackPositionUpdateListener(
//                new AudioCapturer.OnPlaybackPositionUpdateListener() {
//                    @Override
//                    public void onPeriodicNotification(AudioCapturer track) {
//                    }
//
//                    @Override
//                    public void onMarkerReached(AudioCapturer track) {
//                        stop();
//                        if (mListener != null) {
//                            mListener.onCompletion();
//                        }
//                    }
//                });

        mPlayThread = null;
        mKeepPlaying = true;
        mListener = null;
    }

    public SamplePlayer(SoundFile sf) {
        this(sf.getSamples(), sf.getSampleRate(), sf.getChannels(), sf.getNumSamples());
    }

    public void setOnCompletionListener(OnCompletionListener listener) {
        mListener = listener;
    }

    public boolean isPlaying() {
        return mAudioCapturer.getState() == AudioRenderer.State.STATE_PLAYING;
    }

    public boolean isPaused() {
        return mAudioCapturer.getState() == AudioRenderer.State.STATE_PAUSED;
    }

    public void start() {
        if (isPlaying()) {
            return;
        }
        mKeepPlaying = true;
        mAudioCapturer.flush();
        mAudioCapturer.start();
        // Setting thread feeding the audio samples to the audio hardware.
        // (Assumes mChannels = 1 or 2).
        mPlayThread = new Thread() {
            public void run() {
                int position = mPlaybackStart * mChannels;
                mSamples.position(position);
                int limit = mNumSamples * mChannels;
                while (mSamples.position() < limit && mKeepPlaying) {
                    int numSamplesLeft = limit - mSamples.position();
                    if (numSamplesLeft >= mBuffer.length) {
                        mSamples.get(mBuffer);
                    } else {
                        for (int i = numSamplesLeft; i < mBuffer.length; i++) {
                            mBuffer[i] = 0;
                        }
                        mSamples.get(mBuffer, 0, numSamplesLeft);
                    }
                    // TODO(nfaralli): use the write method that takes a ByteBuffer as argument.
                    mAudioCapturer.write(mBuffer, 0, mBuffer.length);
                }
            }
        };
        mPlayThread.start();
    }

    public void pause() {
        if (isPlaying()) {
            mAudioCapturer.pause();
            // mAudioCapturer.write() should block if it cannot write.
        }
    }

    public void stop() {
        if (isPlaying() || isPaused()) {
            mKeepPlaying = false;
            mAudioCapturer.pause();  // pause() stops the playback immediately.
            mAudioCapturer.stop();   // Unblock mAudioCapturer.write() to avoid deadlocks.
            if (mPlayThread != null) {
                try {
                    mPlayThread.join();
                } catch (InterruptedException e) {
                }
                mPlayThread = null;
            }
            mAudioCapturer.flush();  // just in case...
        }
    }

    public void release() {
        stop();
        mAudioCapturer.release();
    }

    public void seekTo(int msec) {
        boolean wasPlaying = isPlaying();
        stop();
        mPlaybackStart = (int) (msec * (mSampleRate / 1000.0));
        if (mPlaybackStart > mNumSamples) {
            mPlaybackStart = mNumSamples;  // Nothing to play...
        }
//        mAudioCapturer.setNotificationMarkerPosition(mNumSamples - 1 - mPlaybackStart);
//        if (wasPlaying) {
//            start();
//        }
    }

    public int getCurrentPosition() {
//        return (int) ((mPlaybackStart + mAudioCapturer.getPlaybackHeadPosition()) *
//                (1000.0 / mSampleRate));
        return 0;
    }
}
